package org.spiderdesign.core.script;

import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spiderdesign.core.expression.ExpressionTemplate;
import org.spiderdesign.core.expression.ExpressionTemplateContext;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ScriptManager {

    private static Logger logger = LoggerFactory.getLogger(ScriptManager.class);

    private static Context context;
    private static Value bindings;

    private static Set<String> functions = new HashSet<>();

    private static ReadWriteLock lock = new ReentrantReadWriteLock();

    public static void setScriptEngine(Context engineContext) {
        context = engineContext;
        bindings = context.getBindings("js");

        // 注册Java类型到JavaScript
        bindings.putMember("ExpressionTemplate", ExpressionTemplate.class);
        bindings.putMember("ExpressionTemplateContext", ExpressionTemplateContext.class);

        // 注册_eval函数
        String evalScript = "function _eval(expression) {" +
                "return ExpressionTemplate.create(expression).render(ExpressionTemplateContext.get());" +
                "}";
        try {
            context.eval("js", evalScript);
            logger.info("注册_eval函数成功");
        } catch (Exception e) {
            logger.error("注册_eval函数失败", e);
        }
    }

    public static void clearFunctions() {
        functions.clear();
    }

    public static Context createEngine() {
        return Context.newBuilder("js")
                .allowAllAccess(true)
                .allowHostAccess(true)
                .allowHostClassLookup(className -> true)
                .build();
    }

    public static void lock() {
        lock.writeLock().lock();
    }

    public static void unlock() {
        lock.writeLock().unlock();
    }

    public static void registerFunction(Context engine, String functionName, String parameters, String script) {
        try {
            String fullScript = concatScript(functionName, parameters, script);
            engine.eval("js", fullScript);
            functions.add(functionName);
            logger.info("注册自定义函数{}成功", functionName);
        } catch (Exception e) {
            logger.warn("注册自定义函数{}失败", functionName, e);
        }
    }

    private static String concatScript(String functionName, String parameters, String script) {
        StringBuilder scriptBuffer = new StringBuilder();
        scriptBuffer.append("function ")
                .append(functionName)
                .append("(")
                .append(parameters == null ? "" : parameters)
                .append("){")
                .append(script)
                .append("}");
        return scriptBuffer.toString();
    }

    public static boolean containsFunction(String functionName) {
        try {
            lock.readLock().lock();
            return functions.contains(functionName);
        } finally {
            lock.readLock().unlock();
        }
    }

    public static void validScript(String functionName, String parameters, String script) throws Exception {
        Context tempContext = createEngine();
        tempContext.eval("js", concatScript(functionName, parameters, script));
        tempContext.close();
    }

    public static Object eval(ExpressionTemplateContext context, String functionName, Object... args) throws Exception {
        if ("_eval".equals(functionName)) {
            if (args == null || args.length != 1) {
                throw new IllegalArgumentException("_eval必须要有一个参数");
            } else {
                return ExpressionTemplate.create(args[0].toString()).render(context);
            }
        }

        if (ScriptManager.context == null) {
            throw new NoSuchMethodException(functionName);
        }

        try {
            lock.readLock().lock();
            Value function = ScriptManager.context.getBindings("js").getMember(functionName);
            if (function == null || !function.canExecute()) {
                throw new NoSuchMethodException("函数 " + functionName + " 不存在或不可执行");
            }

            Value result = function.execute(args);
            return convertObject(result);
        } finally {
            lock.readLock().unlock();
        }
    }

    private static Object convertObject(Value value) {
        if (value == null || value.isNull()) {
            return null;
        }

        if (value.isString()) {
            return value.asString();
        } else if (value.isNumber()) {
            if (value.fitsInInt()) {
                return value.asInt();
            } else if (value.fitsInLong()) {
                return value.asLong();
            } else if (value.fitsInDouble()) {
                return value.asDouble();
            }
        } else if (value.isBoolean()) {
            return value.asBoolean();
        } else if (value.hasArrayElements()) {
            // 处理数组
            int size = (int) value.getArraySize();
            Object[] array = new Object[size];
            for (int i = 0; i < size; i++) {
                array[i] = convertObject(value.getArrayElement(i));
            }
            return array;
        } else if (value.hasMembers()) {
            // 处理对象
            if (value.hasMember("getTime") && value.getMember("getTime").canExecute()) {
                // 处理Date对象
                try {
                    long time = value.getMember("getTime").execute().asLong();
                    return new Date(time);
                } catch (Exception e) {
                    logger.warn("转换Date对象失败", e);
                }
            }
            // 其他对象类型，返回原始Value对象或转换为Map
            return value;
        }

        return value;
    }
}